package top.lixunda.ecommerce.server.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.lixunda.common.utils.ObjectValidators;
import top.lixunda.ecommerce.server.business.BaseServiceImpl;
import top.lixunda.ecommerce.server.business.api.entity.BusinessConstants;
import top.lixunda.ecommerce.server.business.api.entity.cache.CacheAdminUser;
import top.lixunda.ecommerce.server.business.api.entity.cache.CacheLoginUser;
import top.lixunda.ecommerce.server.business.api.entity.cache.CacheStore;
import top.lixunda.ecommerce.server.business.api.entity.cache.CacheStoreUser;
import top.lixunda.ecommerce.server.business.api.entity.dto.PageDTO;
import top.lixunda.ecommerce.server.user.api.entity.db.RoleDO;
import top.lixunda.ecommerce.server.user.api.entity.db.UserRoleDO;
import top.lixunda.ecommerce.server.user.api.entity.dto.RoleChangeDTO;
import top.lixunda.ecommerce.server.user.api.entity.dto.RoleInsertAdminDTO;
import top.lixunda.ecommerce.server.user.api.entity.dto.RoleInsertStoreDTO;
import top.lixunda.ecommerce.server.user.api.entity.dto.RoleQueryDTO;
import top.lixunda.ecommerce.server.user.mapper.RoleMapper;
import top.lixunda.ecommerce.server.user.service.IPermissionService;
import top.lixunda.ecommerce.server.user.service.IRoleService;
import top.lixunda.ecommerce.server.user.service.IUserRoleService;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 角色表，可以代表一种权限，分为官方后台角色和店铺角色，店铺角色每家店铺独享 服务类
 *
 * @author lixunda
 * @version time:2020/4/23 17:06
 */
@Service
public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, RoleDO>
        implements IRoleService {

    private final IUserRoleService userRoleService;

    private final IPermissionService permissionService;

    @Autowired
    public RoleServiceImpl(IUserRoleService userRoleService,
                           IPermissionService permissionService) {
        this.userRoleService = userRoleService;
        this.permissionService = permissionService;
    }

    /**
     * 根据角色编号查询店铺可用角色信息
     *
     * @param id 角色编号
     * @return 角色数据
     */
    @Override
    public RoleDO queryStoreRoleById(Integer id) {
        if (ObjectValidators.isEmpty(id)) {
            throw getAppException("查询角色信息失败！角色编号不能为空！");
        }
        CacheStore cacheStore = currentStoreCacheNotNull();
        return this.getOne(new LambdaQueryWrapper<RoleDO>()
                        .eq(RoleDO::getRoleId, id)
                        .and(innerWrapper -> innerWrapper
                                .or(innerWrapper2 -> innerWrapper2
                                        .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_STORE)
                                        .eq(RoleDO::getStoreId, cacheStore.getId()))
                                .or(innerWrapper2 -> innerWrapper2
                                        .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN)
                                        .eq(RoleDO::getStoreEmploy, BusinessConstants.FLAG_TRUE))),
                false);
    }

    /**
     * 根据角色编号查询官方后台系统可用角色信息
     *
     * @param id 角色编号
     * @return 角色数据
     */
    @Override
    public RoleDO queryAdminRoleById(Integer id) {
        if (ObjectValidators.isEmpty(id)) {
            throw getAppException("查询角色信息失败！角色编号不能为空！");
        }
        currentAdminUserCacheNotNull();
        return this.getOne(new LambdaQueryWrapper<RoleDO>()
                        .eq(RoleDO::getRoleId, id)
                        .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN),
                false);
    }

    /**
     * 根据角色编号查询店铺可用角色信息
     *
     * @param ids 角色编号列表
     * @return 角色数据列表
     */
    @Override
    public List<RoleDO> queryStoreRolesByIds(List<Integer> ids) {
        if (ObjectValidators.isEmpty(ids)) {
            throw getAppException("查询角色信息失败！角色编号不能为空！");
        }
        CacheStore cacheStore = currentStoreCacheNotNull();
        return this.list(new LambdaQueryWrapper<RoleDO>()
                .in(RoleDO::getRoleId, ids)
                .and(innerWrapper -> innerWrapper
                        .or(innerWrapper2 -> innerWrapper2
                                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_STORE)
                                .eq(RoleDO::getStoreId, cacheStore.getId()))
                        .or(innerWrapper2 -> innerWrapper2
                                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN)
                                .eq(RoleDO::getStoreEmploy, BusinessConstants.FLAG_TRUE))));
    }

    /**
     * 根据角色编号查询官方后台系统可用角色信息
     *
     * @param ids 角色编号列表
     * @return 角色数据列表
     */
    @Override
    public List<RoleDO> queryAdminRolesByIds(List<Integer> ids) {
        if (ObjectValidators.isEmpty(ids)) {
            throw getAppException("查询角色信息失败！角色编号不能为空！");
        }
        currentAdminUserCacheNotNull();
        return this.list(new LambdaQueryWrapper<RoleDO>()
                .in(RoleDO::getRoleId, ids)
                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN));
    }

    /**
     * 查询当前登陆店铺可用所有角色信息
     *
     * @return 角色信息列表
     */
    @Override
    public List<RoleDO> queryCurrentStoreAll() {
        CacheStore cacheStore = currentStoreCacheNotNull();
        return this.list(new LambdaQueryWrapper<RoleDO>()
                .and(innerWrapper -> innerWrapper
                        .or(innerWrapper2 -> innerWrapper2
                                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_STORE)
                                .eq(RoleDO::getStoreId, cacheStore.getId()))
                        .or(innerWrapper2 -> innerWrapper2
                                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN)
                                .eq(RoleDO::getStoreEmploy, BusinessConstants.FLAG_TRUE))));
    }

    /**
     * 查询后台管理员可用所有角色信息
     *
     * @return 角色信息列表
     */
    @Override
    public List<RoleDO> queryCurrentAdminAll() {
        currentAdminUserCacheNotNull();
        return this.list(new LambdaQueryWrapper<RoleDO>()
                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN));
    }

    /**
     * 条件分页查询当前登陆店铺可用所有角色信息
     *
     * @param pageDTO 条件分页数据
     * @return 角色信息分页数据
     */
    @Override
    public IPage<RoleDO> queryStoreRolePageWrapper(PageDTO<RoleDO, RoleQueryDTO> pageDTO) {
        if (ObjectValidators.isEmpty(pageDTO)) {
            throw getAppException("查询角色信息失败！条件参数不能为空！");
        }
        CacheStore cacheStore = currentStoreCacheNotNull();
        return this.page(pageDTO.getPage(), pageDTO.getWrapper(wrapper ->
                wrapper.and(innerWrapper -> innerWrapper
                        .or(innerWrapper2 -> innerWrapper2
                                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_STORE)
                                .eq(RoleDO::getStoreId, cacheStore.getId()))
                        .or(innerWrapper2 -> innerWrapper2
                                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN)
                                .eq(RoleDO::getStoreEmploy, BusinessConstants.FLAG_TRUE)))));
    }

    /**
     * 条件分页查询后台管理员可用所有角色信息
     *
     * @param pageDTO 条件分页数据
     * @return 角色信息分页数据
     */
    @Override
    public IPage<RoleDO> queryAdminRolePageWrapper(PageDTO<RoleDO, RoleQueryDTO> pageDTO) {
        if (ObjectValidators.isEmpty(pageDTO)) {
            throw getAppException("查询角色信息失败！条件参数不能为空！");
        }
        return this.page(pageDTO.getPage(), pageDTO.getWrapper(wrapper ->
                wrapper.eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN)));
    }

    /**
     * 添加一条店铺角色信息数据
     *
     * @param list 添加数据DTO列表
     * @return 添加成功数据
     */
    @Override
    public List<RoleDO> insertStoreRole(List<RoleInsertStoreDTO> list) {
        if (ObjectValidators.isEmpty(list)) {
            throw getAppException("添加角色信息失败！参数不能为空！");
        }
        CacheStoreUser cacheStoreUser = currentStoreUserCacheNotNull();
        CacheStore cacheStore = currentStoreCacheNotNull();
        LocalDateTime localDateTime = LocalDateTime.now();
        // 添加角色信息
        List<RoleDO> insert = list.stream()
                .map(item -> {
                    RoleDO roleDO = new RoleDO();
                    BeanUtils.copyProperties(item, roleDO);
                    roleDO.setManageType(BusinessConstants.USER_TYPE_STORE)
                            .setStoreId(cacheStore.getId())
                            .setStoreName(cacheStore.getName())
                            .setStoreEmploy(BusinessConstants.FLAG_TRUE)
                            .setCreateUserId(cacheStoreUser.getAccount())
                            .setCreateUserName(cacheStoreUser.getName())
                            .setCreateUserType(cacheStoreUser.getType())
                            .setCreateTime(localDateTime);
                    return roleDO;
                })
                .collect(Collectors.toList());
        boolean success = this.saveBatch(insert);
        if (!success) {
            throw getAppException("添加角色信息失败！插入数据失败！");
        }
        // 自动插入角色权限数据
        permissionService.insertAutoRolePermission(insert);
        return insert;
    }

    /**
     * 添加一条管理系统角色信息数据
     *
     * @param list 添加数据DTO列表
     * @return 添加成功数据
     */
    @Override
    public List<RoleDO> insertAdminRole(List<RoleInsertAdminDTO> list) {
        if (ObjectValidators.isEmpty(list)) {
            throw getAppException("添加角色信息失败！参数不能为空！");
        }
        CacheAdminUser cacheAdminUser = currentAdminUserCacheNotNull();
        LocalDateTime localDateTime = LocalDateTime.now();
        List<RoleDO> insert = list.stream()
                .map(item -> {
                    RoleDO roleDO = new RoleDO();
                    BeanUtils.copyProperties(item, roleDO);
                    roleDO.setManageType(BusinessConstants.USER_TYPE_ADMIN)
                            .setStoreEmploy(item.getStoreEmploy())
                            .setCreateUserId(cacheAdminUser.getAccount())
                            .setCreateUserName(cacheAdminUser.getName())
                            .setCreateUserType(cacheAdminUser.getType())
                            .setCreateTime(localDateTime);
                    return roleDO;
                })
                .collect(Collectors.toList());
        boolean success = this.saveBatch(insert);
        if (!success) {
            throw getAppException("添加角色信息失败！插入数据失败！");
        }
        // 自动插入角色权限数据
        permissionService.insertAutoRolePermission(insert);
        return insert;
    }

    /**
     * 修改一条店铺角色信息数据
     *
     * @param list         修改数据DTO列表
     * @param solidifyFlag 是否验证固化数据，为true时不能更改固化数据
     * @return 修改成功数据
     */
    private List<RoleDO> changeStoreRoleWithSolidify(List<RoleChangeDTO> list, boolean solidifyFlag) {
        if (ObjectValidators.isEmpty(list)) {
            throw getAppException("修改角色信息失败！参数不能为空！");
        }
        // 鉴权
        CacheStore cacheStore = currentStoreCacheNotNull();
        List<Integer> roleIds = list.stream()
                .map(RoleChangeDTO::getRoleId)
                .collect(Collectors.toList());
        LambdaQueryWrapper<RoleDO> wrapper = new LambdaQueryWrapper<RoleDO>()
                .in(RoleDO::getRoleId, roleIds)
                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_STORE)
                .eq(RoleDO::getStoreId, cacheStore.getId());
        if (solidifyFlag) {
            wrapper.ne(RoleDO::getSolidifyFlag, BusinessConstants.FLAG_TRUE);
        }
        List<RoleDO> select = this.list(wrapper);
        if (ObjectValidators.isEmpty(select) || select.size() != list.size()) {
            throw getAppException("修改角色信息失败！部分角色不存在或权限不足！");
        }
        // 修改数据
        List<RoleDO> update = list.stream()
                .map(item -> {
                    RoleDO roleDO = new RoleDO();
                    BeanUtils.copyProperties(item, roleDO);
                    return roleDO;
                })
                .collect(Collectors.toList());
        boolean success = this.updateBatchById(update);
        if (!success) {
            throw getAppException("修改角色信息失败！修改数据失败！");
        }
        return update;
    }

    /**
     * 修改一条管理系统角色信息数据
     *
     * @param list         修改数据DTO列表
     * @param solidifyFlag 是否验证固化数据，为true时不能更改固化数据
     * @return 修改成功数据
     */
    private List<RoleDO> changeAdminRoleWithSolidify(List<RoleChangeDTO> list, boolean solidifyFlag) {
        if (ObjectValidators.isEmpty(list)) {
            throw getAppException("修改角色信息失败！参数不能为空！");
        }
        // 鉴权
        currentAdminUserCacheNotNull();
        List<Integer> roleIds = list.stream()
                .map(RoleChangeDTO::getRoleId)
                .collect(Collectors.toList());
        LambdaQueryWrapper<RoleDO> wrapper = new LambdaQueryWrapper<RoleDO>()
                .in(RoleDO::getRoleId, roleIds)
                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN);
        if (solidifyFlag) {
            wrapper.ne(RoleDO::getSolidifyFlag, BusinessConstants.FLAG_TRUE);
        }
        List<RoleDO> select = this.list(wrapper);
        if (ObjectValidators.isEmpty(select) || select.size() != list.size()) {
            throw getAppException("修改角色信息失败！部分角色不存在或权限不足！");
        }
        // 修改数据
        List<RoleDO> update = list.stream()
                .map(item -> {
                    RoleDO roleDO = new RoleDO();
                    BeanUtils.copyProperties(item, roleDO);
                    return roleDO;
                })
                .collect(Collectors.toList());
        boolean success = this.updateBatchById(update);
        if (!success) {
            throw getAppException("修改角色信息失败！修改数据失败！");
        }
        return update;
    }

    /**
     * 根据角色编号移除角色分配信息
     *
     * @param roleIds 角色编号列表
     * @return 是否移除成功
     */
    private Boolean removeUserRole(List<Integer> roleIds) {
        LocalDateTime localDateTime = LocalDateTime.now();
        CacheLoginUser cacheLoginUser = currentLoginUserCacheNotNull();
        boolean success = userRoleService.update(new LambdaUpdateWrapper<UserRoleDO>()
                .set(UserRoleDO::getUsable, BusinessConstants.USEABLE_FALSE)
                .set(UserRoleDO::getRemoveTime, localDateTime)
                .set(UserRoleDO::getRemoveUserId, cacheLoginUser.getAccount())
                .set(UserRoleDO::getRemoveUserName, cacheLoginUser.getName())
                .set(UserRoleDO::getRemoveUserType, cacheLoginUser.getType())
                .in(UserRoleDO::getRoleId, roleIds)
                .eq(UserRoleDO::getUsable, BusinessConstants.USEABLE_TRUE));
        if (!success) {
            throw getAppException("移除用户角色信息失败！");
        }
        return true;
    }

    /**
     * 根据角色编号移除店铺角色信息
     *
     * @param roleIds      角色编号列表
     * @param solidifyFlag 是否验证固化数据，为true时不能更改固化数据
     * @return 是否移除成功
     */
    private Boolean removeStoreRoleWithSolidify(List<Integer> roleIds, boolean solidifyFlag) {
        if (ObjectValidators.isEmpty(roleIds)) {
            throw getAppException("删除角色信息失败！角色编号不能为空！");
        }
        // 鉴权
        CacheStore cacheStore = currentStoreCacheNotNull();
        CacheStoreUser cacheStoreUser = currentStoreUserCacheNotNull();
        LocalDateTime localDateTime = LocalDateTime.now();
        LambdaQueryWrapper<RoleDO> wrapper = new LambdaQueryWrapper<RoleDO>()
                .in(RoleDO::getRoleId, roleIds)
                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_STORE)
                .eq(RoleDO::getStoreId, cacheStore.getId());
        if (solidifyFlag) {
            wrapper.ne(RoleDO::getSolidifyFlag, BusinessConstants.FLAG_TRUE);
        }
        List<RoleDO> select = this.list(wrapper);
        if (ObjectValidators.isEmpty(select) || select.size() != roleIds.size()) {
            throw getAppException("删除角色信息失败！部分角色不存在或权限不足！");
        }
        // 修改数据
        List<RoleDO> update = select.stream()
                .map(item -> item.setRemoveUserId(cacheStoreUser.getAccount())
                        .setRemoveUserName(cacheStoreUser.getName())
                        .setRemoveUserType(cacheStoreUser.getType())
                        .setRemoveTime(localDateTime))
                .collect(Collectors.toList());
        boolean success = this.updateBatchById(update);
        if (!success) {
            throw getAppException("删除角色信息失败！修改数据失败！");
        }
        success = this.removeByIds(roleIds);
        if (!success) {
            throw getAppException("删除角色信息失败！删除数据失败！");
        }
        // 移除角色自动分配的权限信息
        success = permissionService.removeAutoRolePermission(roleIds);
        if (!success) {
            throw getAppException("删除角色信息失败！删除角色权限分配信息失败！");
        }
        // 移除用户角色分配信息
        success = removeUserRole(roleIds);
        if (!success) {
            throw getAppException("删除角色信息失败！移除用户角色分配信息失败！");
        }
        return true;
    }

    /**
     * 根据角色编号移除管理系统角色信息
     *
     * @param roleIds      角色编号列表
     * @param solidifyFlag 是否验证固化数据，为true时不能更改固化数据
     * @return 是否移除成功
     */
    private Boolean removeAdminRoleWithSolidify(List<Integer> roleIds, boolean solidifyFlag) {
        if (ObjectValidators.isEmpty(roleIds)) {
            throw getAppException("删除角色信息失败！角色编号不能为空！");
        }
        // 鉴权
        CacheAdminUser cacheAdminUser = currentAdminUserCacheNotNull();
        LocalDateTime localDateTime = LocalDateTime.now();
        LambdaQueryWrapper<RoleDO> wrapper = new LambdaQueryWrapper<RoleDO>()
                .in(RoleDO::getRoleId, roleIds)
                .eq(RoleDO::getManageType, BusinessConstants.USER_TYPE_ADMIN);
        if (solidifyFlag) {
            wrapper.ne(RoleDO::getSolidifyFlag, BusinessConstants.FLAG_TRUE);
        }
        List<RoleDO> select = this.list(wrapper);
        if (ObjectValidators.isEmpty(select) || select.size() != roleIds.size()) {
            throw getAppException("删除角色信息失败！部分角色不存在或权限不足！");
        }
        // 修改数据
        List<RoleDO> update = select.stream()
                .map(item -> item.setRemoveUserId(cacheAdminUser.getAccount())
                        .setRemoveUserName(cacheAdminUser.getName())
                        .setRemoveUserType(cacheAdminUser.getType())
                        .setRemoveTime(localDateTime))
                .collect(Collectors.toList());
        boolean success = this.updateBatchById(update);
        if (!success) {
            throw getAppException("删除角色信息失败！修改数据失败！");
        }
        success = this.removeByIds(roleIds);
        if (!success) {
            throw getAppException("删除角色信息失败！删除数据失败！");
        }
        // 移除角色自动分配的权限信息
        success = permissionService.removeAutoRolePermission(roleIds);
        if (!success) {
            throw getAppException("删除角色信息失败！删除角色权限分配信息失败！");
        }
        // 移除用户角色分配信息
        success = removeUserRole(roleIds);
        if (!success) {
            throw getAppException("删除角色信息失败！移除用户角色分配信息失败！");
        }
        return true;
    }

    /**
     * 修改一条店铺角色信息数据
     *
     * @param list 修改数据DTO列表
     * @return 修改成功数据
     */
    @Override
    public List<RoleDO> changeStoreRole(List<RoleChangeDTO> list) {
        return this.changeStoreRoleWithSolidify(list, true);
    }

    /**
     * 修改一条管理系统角色信息数据
     *
     * @param list 修改数据DTO列表
     * @return 修改成功数据
     */
    @Override
    public List<RoleDO> changeAdminRole(List<RoleChangeDTO> list) {
        return this.changeAdminRoleWithSolidify(list, true);
    }

    /**
     * 根据角色编号移除店铺角色信息
     *
     * @param roleIds 角色编号列表
     * @return 是否移除成功
     */
    @Override
    public Boolean removeStoreRole(List<Integer> roleIds) {
        return this.removeStoreRoleWithSolidify(roleIds, true);
    }

    /**
     * 根据角色编号移除管理系统角色信息
     *
     * @param roleIds 角色编号列表
     * @return 是否移除成功
     */
    @Override
    public Boolean removeAdminRole(List<Integer> roleIds) {
        return this.removeAdminRoleWithSolidify(roleIds, true);
    }

    /**
     * 修改一条店铺角色信息数据，固化数据修改
     *
     * @param list 修改数据DTO列表
     * @return 修改成功数据
     */
    @Override
    public List<RoleDO> changeStoreSolidifyRole(List<RoleChangeDTO> list) {
        return this.changeStoreRoleWithSolidify(list, false);
    }

    /**
     * 修改一条管理系统角色信息数据，固化数据修改
     *
     * @param list 修改数据DTO列表
     * @return 修改成功数据
     */
    @Override
    public List<RoleDO> changeAdminSolidifyRole(List<RoleChangeDTO> list) {
        return this.changeAdminRoleWithSolidify(list, false);
    }

    /**
     * 根据角色编号移除店铺角色信息，固化数据移除
     *
     * @param roleIds 角色编号列表
     * @return 是否移除成功
     */
    @Override
    public Boolean removeStoreSolidifyRole(List<Integer> roleIds) {
        return this.removeStoreRoleWithSolidify(roleIds, false);
    }

    /**
     * 根据角色编号移除管理系统角色信息，固化数据移除
     *
     * @param roleIds 角色编号列表
     * @return 是否移除成功
     */
    @Override
    public Boolean removeAdminSolidifyRole(List<Integer> roleIds) {
        return this.removeAdminRoleWithSolidify(roleIds, false);
    }

}
